﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using DynamicData;
using GitNet.WindowsApp.AddViews;
using GitNet.WindowsApp.Models;
using LibGit2Sharp;
using MessageBox.Avalonia;
using MessageBox.Avalonia.Enums;

namespace GitNet.WindowsApp.ViewModels;

public class RepositoryViewModel : ViewModelBase
{
    
    #region Network
    
    private Remote? _remote;

    public Remote? Remote
    {
        get => _remote;
        set
        {
            SetField(ref _remote, value);
            Content = _remote;
        }
    }

    #endregion

    #region InfoPror

    private string? _name;

    public string? Name
    {
        get => _name;
        set => SetField(ref _name, value);
    }

    private string? _email;

    public string? Email
    {
        get => _email;
        set => SetField(ref _email, value);
    }

    private string? _password;

    public string? Password
    {
        get => _password;
        set => SetField(ref _password, value);
    }

    private string? _account;

    public string? Account
    {
        get => _account;
        set => SetField(ref _account, value);
    }

    #endregion

    #region AddCommitPror

    private string? _commitMessage;

    public string? CommitMessage
    {
        get => _commitMessage;
        set => SetField(ref _commitMessage, value);
    }

    private bool _isCheck;

    public bool IsCheck
    {
        get => _isCheck;
        set
        {
            SetField(ref _isCheck, value);
            if (!value) return;
            for (int i = 0; i < Booleans.Length; i++) Booleans[i] = value;
        }
    }

    private bool[] Booleans { get; set; }

    #endregion

    #region InitPror

    private string? _repositoryPath;

    public string? RepositoryPath
    {
        get => _repositoryPath;
        private set => SetField(ref _repositoryPath, value);
    }
    
    public ObservableCollection<Commit> Commits { get; set; }
    public ObservableCollection<Branch> Branches { get; set; }
    public ObservableCollection<StatusEntry> StatusFiles { get; set; }
    public ObservableCollection<Tag> Tags { get; set; }
    public ObservableCollection<Remote> Remotes { get; set; }
    private Repository Repository { get; set; }

    #endregion

    #region BasicPror

    private object? _content;

    public object? Content
    {
        get => _content;
        set => SetField(ref _content, value);
    }

    private object? _selection;

    public object? Selection
    {
        get => _selection;
        set
        {
            SetField(ref _selection, value);
            if (value == null) return;
            if (value is Commit commit)
            {
                Content = commit;
                return;
            }

            if (value is Branch branch)
            {
                Content = branch;
                return;
            }

            if (value is StatusEntry status)
            {
                var a = GetPatch(status.FilePath);
                Content = new StatusModel()
                {
                    AddContext = a.AddedLines,
                    DeleteContext = a.DeletedLines,
                    State = status.State.ToString(),
                    FilePath = status.FilePath
                };
            }
        }
    }

    #endregion

    #region RepositoryFunction

    public PatchEntryChanges GetPatch(string fileName)
    {
        var patch = Repository.Diff.Compare<Patch>(new List<string>() { fileName });
        return patch.First();
    }

    private void Add(string path)
    {
        Repository.Index.Add(path);
        Repository.Index.Write();
    }

    public void AddTag(string sha, string tabName, string message)
    {
        if (string.IsNullOrEmpty(message))
            Repository.ApplyTag(tabName, sha);
        else
            Repository.ApplyTag(tabName, sha, new Signature(Name, Email, DateTime.Now), message);
        Tags.Clear();
        Tags.Add(Repository.Tags);
    }

    public void Push()
    {
        if (string.IsNullOrEmpty(Account) || string.IsNullOrEmpty(Password)) return;
        var options = new PushOptions
        {
            CredentialsProvider = (_url, _user, _cred)
                => new UsernamePasswordCredentials { Username = Account, Password = Password }
        };
        var refSpecs = Remote?.PushRefSpecs.Select(x => x.Specification);
        Repository.Network.Push(Remote, refSpecs, options);
    }

    private void Add()
    {
        Commands.Stage(Repository, "*");
    }

    public async void CreateBranch(string branchName, int index)
    {
        var messageBoxStandardWindow = MessageBoxManager.GetMessageBoxStandardWindow("Are You Sure?", "Will be Create",
            ButtonEnum.OkCancel, Icon.Warning);
        var buttonResult = await messageBoxStandardWindow.Show();
        if (buttonResult == ButtonResult.Cancel) return;
        if (index >= Branches.Count) return;
        if (index == 0)
        {
            Repository.CreateBranch(branchName);
            return;
        }

        Repository.CreateBranch(branchName, $"HEAD~{index}");
    }

    public async void Reset(Commit commit)
    {
        var messageBoxStandardWindow =
            MessageBoxManager.GetMessageBoxStandardWindow("Warning", "Will be Reset", ButtonEnum.YesNo, Icon.Warning);
        var buttonResult = await messageBoxStandardWindow.Show();
        if (buttonResult == ButtonResult.No) return;
        if (commit == Repository.Head.Tip) return;
        Repository.Reset(ResetMode.Mixed, commit);
    }

    public async void RemoveBranch(string branchName)
    {
        var messageBoxStandardWindow =
            MessageBoxManager.GetMessageBoxStandardWindow("Warning", "Will be Remove", ButtonEnum.YesNo, Icon.Warning);
        var buttonResult = await messageBoxStandardWindow.Show();
        if (buttonResult == ButtonResult.No) return;
        if (branchName == "master") return;
        Repository.Branches.Remove("BranchToDelete");
    }

    public async void Checkout(string branchName)
    {
        var messageBoxStandardWindow =
            MessageBoxManager.GetMessageBoxStandardWindow("Warning", "Will be Checkout", ButtonEnum.YesNo,
                Icon.Warning);
        var buttonResult = await messageBoxStandardWindow.Show();
        if (buttonResult == ButtonResult.No) return;
        var branch = Repository.Branches[branchName];
        if (branch == null) return;
        if (branch.FriendlyName == "origin") return;
        if (branch.FriendlyName == Repository.Head.FriendlyName) return;
        Commands.Checkout(Repository, branch);
    }

    private void Commit(string message)
    {
        Signature author = new Signature(Name, Email, DateTime.Now);
        Signature committer = author;
        Repository.Commit(message, author, committer);
    }

    #endregion

    #region Function

    public void SetBooleans(bool value, int index)
    {
        Booleans.SetValue(value, index);
        _isCheck = Booleans.All(x => x);
    }

    private void Flushed()
    {
        Commits.Clear();
        Commits.Add(Repository.Commits);
        Branches.Clear();
        Branches.Add(Repository.Branches);
        StatusFiles.Clear();
        StatusFiles.Add(Repository.RetrieveStatus()
            .Where(x => x.State != FileStatus.Ignored));

        Booleans = new bool[StatusFiles.Count];
        IsCheck = false;
        CommitMessage = "";
    }

    #endregion

    #region CommandFuntion

    public void AddCommitCommand()
    {
        if (string.IsNullOrEmpty(CommitMessage)) return;
        if (!IsCheck && Booleans.All(x => !x)) return;
        if (IsCheck || Booleans.All(x => x))
        {
            Add();
            StatusFiles.Clear();
        }
        else
        {
            for (int i = 0; i < Booleans.Length; i++)
            {
                if (Booleans[i])
                {
                    Add(StatusFiles[i].FilePath);
                    StatusFiles.RemoveAt(i);
                }
            }
        }

        Commit(CommitMessage);
        Flushed();
    }

    public void CreateBranchCommand()
    {
        Content = new AddBranchView();
    }

    public void SaveInfoCommand()
    {
        var json = ProjectData.OpenJson();
        if (json == null) return;
        json.UserName = Name;
        json.Email = Email;
        json.Account = Account;
        json.Password = Password;
        ProjectData.Save(json);
    }

    public void AddTagCommand(string sha)
    {
        Content = new AddTagView(sha);
    }

    #endregion

    public RepositoryViewModel(string path)
    {
        RepositoryPath = path;
        Repository = new Repository(path);
        Commits = new ObservableCollection<Commit>(Repository.Commits);
        Branches = new ObservableCollection<Branch>(Repository.Branches);
        StatusFiles =
            new ObservableCollection<StatusEntry>(Repository.RetrieveStatus()
                .Where(x => x.State != FileStatus.Ignored));
        Tags = new ObservableCollection<Tag>(Repository.Tags);
        Remotes = new ObservableCollection<Remote>(Repository.Network.Remotes);

        Booleans = new bool[StatusFiles.Count];
        Email = Repository.Config.FirstOrDefault(x => x.Key == "user.email")?.Value;
        Name = Repository.Config.FirstOrDefault(x => x.Key == "user.name")?.Value;
        var a = ProjectData.OpenJson();
        if (a == null) return;
        Account = a.Account;
        Password = a.Password;
    }
    
}